Introduction
This course is an introduction to performing Linux system configuration and hardening reviews, it details all the steps and provides examples of issues that can usually be identified on a Linux server. More than just an how-to check a Linux system, this course should be seen as a general way to approach a host review and understand what security issues are present.
Host review
Understanding the system roles
Before starting any configuration review, it's important to understand what the system is used for. The exposure and security requirements will differ based on the system purpose, for example, a web server on the Internet and a file server in your internal network have totally different security models. Here the system is a web server available on Internet and it's used to serve a PHP web application (a Wordpress blog). Nothing else is used or needed on this system.
This system is supposed to be available on Internet, it's therefore likely to be attacked remotely and will almost certainly have brute-force attacks performed against it.
Access needed
In order to perform a host review you will need root or equivalent access to the system. Performing a system review without this level of privilege will prevent you from doing a serious and comprehensive work.
Here you have a shell on the console running as user, you can connect remotely using ssh with the password live. Once connected, you can get root access using sudo -s.
Automatic deployment
More and more systems are deployed using automatic deployment like cfengine, Puppet or Chef. This won't change any issue found during the review, however the remediation should be applied to the deployer's configuration not on the system itself. Otherwise, new systems (or even this system) may be incorrectly configured or re-configured.
Taking notes
It's really important to take notes during a host review, a good way to keep notes during the review is to use screen logs. This can be done on the audited system directly or on your system.
First, you need to launch screen (if you decided to do this on the audited system, go to /tmp first):
$ screenYou can then start the logging by hitting Ctrl-a and then H, the following should appear:
Creating logfile "screenlog.0"Then you can start the system review. Once you are done, you can use the same key combination to stop the logging: Ctrl-a and then H. The following should now appear:
Logfile "screenlog.0" closed.You can then rename the file to save it:
# mv screenlog.0 Audit-`hostname`-`date +"%d-%b-%Y_%H:%M"`.txtFor all commands ran during the review, it's good to keep their output. A good way to do this is to create a directory in /tmp and put everything inside:
# mkdir /tmp/auditAnd then run all commands one time to get the information and another time to save the results (standard output and standard error) to a file in this directory:
# uname
Linux
# uname &> /tmp/audit/uname.txtFinally, if you need to read files, open them in read-only mode:
- cat fileand- grepthe output.
- vi:- vi -R file.
- nano:- nano -v file.
This will prevent you from modifying files you really shouldn't!
System review
Operating system
The system you are auditing is a Debian 6 system, you can get this information by retrieving the content of /etc/debian_version. On other Linux based systems you can:
- Read the content of /etc/redhat-releasefor RedHat based systems like Redhat, CentOS and Fedora (/etc/fedora-releaseworks as well).
- Run lsb_release -aon Ubuntu based systems.
Debian 6 is the current stable version so you don't have to worry about end of life. But for old versions, it's always important to check if the version is still supported to know if new vulnerabilities will be patched. For example, the previous version of Debian (Lenny) is no longer supported since February 2012 (https://wiki.debian.org/DebianLenny).
Kernel
The Kernel version can be retrieved by using the command uname:
# uname -a
Linux debian 2.6.32-5-amd64 #1 SMP Sun May 6 04:00:17 UTC 2012 x86_64 GNU/LinuxBased on this version information, you need to identify whether any vulnerabilities have been published for this specific version. Make sure that you are checking this version against the distribution vulnerabilities list, as most Linux distributions back-port patches without changing the kernel major and minor version.
Checking the uptime of a Linux system can also be a good indicator on when the last kernel upgrade was performed:
# uptime
21:14:58 up 70 days,  6:20, 21 users,  load average: 0.74, 0.51, 0.58Here we can see that the system has been up for 70 days, the kernel is really unlikely to have been patched during this period.
Time management
It'is really important, for all operations based on the time, that a system is correctly synchronised with an NTP server:
- Logs management.
- SSL verification of certificates.
- Authentication based on the time.
Furthermore, for sensitive production systems it's always better to use a timezone that doesn't present daylight savings to avoid time jumps in the logs. Time jumps are bad as they prevent the accurate correlation between multiple log sources.
Here, we can see that the timezone is configured to UTC:
# cat /etc/timezone
Etc/UTCAnother thing is to use an NTP server instead of setting the time manually using ntpdate as:
- The time will stay synchronised.
- There is no risk of a time jump occurring in the logs.
Here, we can see that an NTP server is running:
# ps -edf | grep ntp
ntp       1397     1  0 02:21 ?        00:00:00 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 102:104We however need to check that the NTP server can access the servers it's trying to connect to:
# ntpq  -p -n
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
-121.0.0.41      204.152.184.72   2 u  250 1024  377   54.635   -2.797   0.877
+203.26.72.7     202.147.104.51   3 u  393 1024  377   58.965   -0.952   9.207
*130.102.128.23  130.102.132.164  2 u  495 1024  377   59.171    0.984   2.292
+208.87.107.28   206.246.122.250  2 u  461 1024  377  272.890    1.948   1.136Here, it can correctly access its peers.
NTP can also use keys for servers authentication in extremely sensitive environments.
Installed Packages
It's always a good thing to check that the number of packages is limited to the strict minimum to limit the system exposure. Furthermore, since the audited system is used as a web server, some packages, such as those related to the graphical interface (X, Gnome, KDE) or games are not needed and should not be installed.
On Debian, you can retrieve a list of installed packages using the following command:
# dpkg -l | less
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                                Version                      Description
+++-===================================-============================-===================================================================
ii  adduser                             3.112+nmu2                   add and remove users and groups
ii  apache2                             2.2.16-6+squeeze7            Apache HTTP Server metapackage
ii  apache2-mpm-prefork                 2.2.16-6+squeeze7            Apache HTTP Server - traditional non-threaded model
ii  apache2-utils                       2.2.16-6+squeeze7            utility programs for webservers
ii  apache2.2-bin                       2.2.16-6+squeeze7            Apache HTTP Server common binary files
[ ... ]You can then search this list for unnecessary packages or those presenting vulnerabilities.
Logging
Another thing that needs to be checked is how the logging of events is performed. On this system, we can see that rsyslog is used:
# ps -edf | grep syslog
root      1305     1  0 Aug06 ?        00:00:00 /usr/sbin/rsyslogd -c4rsyslog configuration is stored in /etc/rsyslog.conf. By reviewing the configuration file, we can see that rsyslog on this system is not configured to receive logs:
# provides UDP syslog reception
#$ModLoad imudp
#$UDPServerRun 514
# provides TCP syslog reception
#$ModLoad imtcp
#$InputTCPServerRun 514The configuration used for files and directories permissions is restricted:
# Set the default permissions for all log files.
#
$FileOwner root
$FileGroup adm
$FileCreateMode 0640
$DirCreateMode 0755
$Umask 0022
However, none of the logging configuration is set to perform logging to a remote system. Unless logs are backed up by a remote system, it's always better to configure remote logging for servers, in rsyslog it's performed by @servername.
Network review
General information
It's always good to retrieve, examine, and store basic system configuration information before doing any other checks. I keep the output of the following commands:
- ifconfig -a: to see what network interfaces are present.
- route -n: to get the system routes (if the system does not have the route command installed,- netstat -rncan be a suitable alternative).
- cat /etc/resolv.confand- cat /etc/hosts: to know more about the DNS configuration of the system.
Once you have this information, you will be able to review the firewall and services configurations.
Firewall rules
For firewall rules you need to check 2 things:
- The firewall rules themselves.
- If the rules will persist across reboots.
Here we can retrieve the firewall rules using iptables:
# iptables -L -v
hain INPUT (policy DROP 2 packets, 1152 bytes)
pkts bytes target     prot opt in     out     source               destination
  40  3696 ACCEPT     all  --  lo     any     anywhere             anywhere
  86  7401 ACCEPT     tcp  --  eth0   any     anywhere             anywhere            tcp dpt:ssh
   0     0 ACCEPT     tcp  --  eth0   any     anywhere             anywhere            tcp dpt:www
  29  4818 ACCEPT     all  --  any    any     anywhere             anywhere            state RELATED,ESTABLISHED
hain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target     prot opt in     out     source               destination
hain OUTPUT (policy ACCEPT 16 packets, 1135 bytes)
pkts bytes target     prot opt in     out     source               destination         We can see that these rules allow in-going traffic:
- Access from everywhere to the port 80(HTTP).
- Access from everywhere to the port 22(SSH).
- Access for already known traffic (response to connections initiated by the server itself).
The port 80 (HTTP) will need to be accessed by anyone on Internet to visit the hosted website. However the port 22 (SSH) should only be accessed by the server administrators. A white-list of trusted IP addresses should be created to limit the access to the SSH server.
We can also see that no rules are applied to the outgoing traffic. It's a best practice to limit outgoing traffic as much as possible by restricting the servers access to the Internet and other internal hosts. Limiting outgoing traffic is likely to slow down an intruder and provide an additional layer of defense-in-depth. For our system review, the server should only allows access on the way out to:
- Already established connections: like packets going back to the client after they connect to a service.
- DNS serversfor name resolution.
- Debian HTTP serversfor software updates.
- NTP serversfor time synchronisation.
We also need to check that the firewall rules will be applied automatically every time the server is started. The Debian way of doing this is to create a /etc/network/if-pre-up.d/iptables file containing the commands to load the rules:
# cat etc/network/if-pre-up.d/iptables
#!/bin/bash
/sbin/iptables-restore < /etc/iptables.up.rulesWe need to check that the file used to load the rules and the current rules match:
# cat /etc/iptables.up.rules
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [27:3016]
-A INPUT -i lo -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
COMMITHere we can see that the same rules are applied. It's however very common to get different rules on systems or even on devices (like routers or firewalls). That's why it's really important to make sure they correctly match.
Ipv6
We can check that there is no firewall rule for IPv6 on this host:
# ip6tables -L -v
hain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target     prot opt in     out     source               destination
hain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target     prot opt in     out     source               destination
hain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target     prot opt in     out     source               destination         At least the same firewall rules applied to IPv4 should be applied for IPv6. If IPv6 is not necessary on this server, it can be disabled, on Debian, by creating a file named /etc/sysctl.d/disableipv6.conf that contains the following net.ipv6.conf.all.disable_ipv6=1.
File system review
Mounted partitions
Here the live CD doesn't have a "normal" file system. But this is how you can check that the configuration is secure on a "normal" system. For each entries in the /etc/fstab, you need to review that:
- noatimeis not used.- noatimeprevents update of inode access time. In case on intrusion, it's always a good idea to have this information.
- For file systems like /tmpor/home, you can usenoexecandnosuidto prevent users from executing binaries and respectively to preventsetuidto be interpreted. It's also recommended thatnosuidis included for/dev, if/devis usingdevfs.
Sensitive files
It's important to check permission on sensitive files. As a general rule, you want to check:
- That files containing sensitive information (passwords, private keys) cannot be read by any user.
- That files containing configurations cannot be modified by any user.
For example, the following files shouldn't be readable by any user:
- /etc/shadowcontaining users' passwords (hashed).
- /etc/mysql/my.cnfcontaining- debian-sys-maint's password.
- SSL private keysused by Apache.
The same obviously applies to any copy of these files, here we can see that a file named /etc/shadow.backup is present on the file system and can be read by any user. Even if the permissions are correctly set on the /etc/shadow file, this "backup" file strongly reduces the security of the system.
Setuid
Setuid files are files ran with the owner's privileges, as opposed to normal binaries that are run with current user's privileges. These files are obviously potential security risks, especially if their owner is root. A common example of a setuid file is the utility passwd. In order for users to change their password, they need the access to the /etc/shadow file, but they cannot be allowed to read the file directly, since it contains the hashed passwords for all users. The setuid bit being set means that this executable will execute as the owner of the file (in this case the root user), allowing /etc/shadow to be read and modified without allowing the user to directly access the content of this file.
You can retrieve a list of the setuid files on the system by running the following command:
# find / -perm -4000 -ls
[...]
4948   15 -rwsr-sr-x   1 libuuid  libuuid  15208 Jan 25  2011 /usr/sbin/uuidd
[...]
41584   34 -rwsr-xr-x   1 root     root     34248 Oct 14  2010 /bin/ping
41585   36 -rwsr-xr-x   1 root     root     36640 Oct 14  2010 /bin/ping6
41590   34 -rwsr-xr-x   1 root     root     34024 Feb 15  2011 /bin/su
41595   53 -rwsr-xr-x   1 root     root     53648 Jan 25  2011 /bin/umountFor each file found, you need to check if this file is legitimate and if its permissions are set correctly.
Normal files
Using find, you can retrieve a list of files that are readable and write-able by any user using:
# find / -type f -perm -006 2>/dev/nulland a list of files write-able by any user using:
# find / -type f -perm -002 2>/dev/nullYou probably want to filter out everything in /proc using  | grep -v /proc after the commands above. Once you have done that, you can see that the file in /var/www/ can be read from and written to by any user. This is likely to ease the progress of an attacker gaining illegitimate access to the system.
Backup
A common mistake performed by system administrators comes from backups. Too often, the backup files can be read by unprivileged users.
On this system, we can see that a directory named /backup is present, if we check the permissions of its content we can see that the files can be read by any user. An attacker can use this to extract etc.tgz and read the shadow file. It's important to change permissions on these files and on the /backup directory and to make sure that the script used for backup correctly set the permissions every time it gets called.
Users review
Reviewing the passwd file
A common misconfiguration/backdoor of the /etc/passwd is to have a user with the uid 0. On traditional systems, only root is supposed to have the uid 0. If another user has the uid 0, they are basically root on the system.
When reviewing /etc/passwd, it's always good to check which users have a shell (/bin/bash, /bin/sh ...) and which don't (/bin/false, /usr/sbin/nologin ...), restricting shell access will ensure that the user cannot connect and run commands on the system.
Reviewing the shadow file
In the /etc/shadow, the most important is to review the algorithms used for passwords encryption and passwords' quality.
You can check what algorithm is used by checking the hash format, if the hash:
- Does not have a $sign,DESis used.
- Starts by $1$,MD5is used.
- Starts by $2$or$2a$,Blowfishis used.
- Starts by $5$,SHA-256is used.
- Starts by $6$,SHA-512is used.
On modern Linux systems, you should avoid DES and MD5. DES algorithm for passwords encryption will limit the size of the password to 8 characters and is really easy to crack. MD5 doesn't present the same length weakness but is pretty quick to brute force compared to other algorithms.
You can review what is the default algorithm used to encrypt password by checking the content of /etc/pam.d/common-password. Here we can see that sha512 is used:
$ cat /etc/pam.d/common-password
[...]
password  [success=1 default=ignore]  pam_unix.so obscure sha512
[...]To improve the system security, you can add some requirements on a password by installing libpam-cracklib:
# apt-get install libpam-cracklibTo enforce the complexity, you can play with the parameter minlen and lcredit, ucredit, dcredit and ocredit to force the number of lowercase, uppercase and special characters and digits in a password.
After installing libcrack, the following line has been added to the /etc/pam.d/common-password file to enforce the complexity a password:
$ cat /etc/pam.d/common-password
[...]
password        requisite                       pam_cracklib.so retry=3 minlen=8 difok=4
[...]We can see here another important parameter: difok, that tells pam how many characters in the new password must be different from the previous one.
To force the number of special characters and digits in a password, you can change the line above to:
$ cat /etc/pam.d/common-password
[...]
password        requisite                       pam_cracklib.so retry=3 minlen=8 difok=4 ocredit=-2 dcredit=-1
[...]It will enforce one digit and two special characters in a password. You can read more about pam_cracklib configuration by running: man pam_cracklib.
John-The-Ripper can be used to crack this password, most modern Linux distribution include a version of john, in order to crack this password you need to tell john what algorithm has been used for encryption.
In most Linux distributions, the version of John-The-Ripper provided only supports a small number of formats. You can run john without any arguments to get a list of the supported formats from the usage information. For example on Fedora, the following formats are supported:
$ john
# ...usage information...
--format=NAME              force hash type NAME: DES/BSDI/MD5/BF/AFS/LM/crypt
# ...usage information...Here, we saw that we have two formats used. Using the john installed on this system, you can crack the user password since DES is supported by this version of john:
# john passwordUnfortunately (for the attacker), the root password is using SHA-512. In order to crack this password, we will need a version of john supporting crypt. The community-enhanced version supports crypt and can be used.
The following command line can be used to crack the password previously retrieved:
$ ./john password --format=crypt  --wordlist=dico --rulesThe following options are used:
- password: tells- johnwhat file contains the password hash.
- --format=crypt: tells- johnthat the password hash can be cracked using- crypt.
- --wordlist=dico: tells john to use the file- dicoas a wordlist.
- --rules: tells- johnto try variations for each provided word.
John-The-Ripper has an opportunistic behavior regarding algorithm selection, it will take the first it recognizes. You need to make sure that you run john with each of the format available in the password file.
Reviewing the sudo configuration
The sudo configuration is stored in /etc/sudoers:
# egrep -v '^#|^$'  /etc/sudoers
Defaults  env_reset
root  ALL=(ALL) ALL
%sudo ALL=(ALL) ALL
user  ALL=(ALL) NOPASSWD: ALLWe can see there that the user named user can be any user (including root) without any password and without restrictions.
A common mistake with sudo is to provide a user with a limited set of commands that will still allow them to get a root shell on the system. For example, a user with access to /bin/chown (change owner) and /bin/chmod (change mode) will be able to copy a shell in their home and change the shell owner to root and add the setuid bit on the file. This way, this user will be able to have a root shell on the system.
Services review
Identifying running services
Using ps, you can quickly identify running services:
# ps -edf
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 02:21 ?        00:00:00 init [2]
root         2     0  0 02:21 ?        00:00:00 [kthreadd]
root         3     2  0 02:21 ?        00:00:00 [migration/0]
root         4     2  0 02:21 ?        00:00:00 [ksoftirqd/0]
root         5     2  0 02:21 ?        00:00:00 [watchdog/0]
root         6     2  0 02:21 ?        00:00:00 [events/0]
root         7     2  0 02:21 ?        00:00:00 [cpuset]
root         8     2  0 02:21 ?        00:00:00 [khelper]
root         9     2  0 02:21 ?        00:00:00 [netns]
root        10     2  0 02:21 ?        00:00:00 [async/mgr]
root        11     2  0 02:21 ?        00:00:00 [pm]
root        12     2  0 02:21 ?        00:00:00 [sync_supers]
root        13     2  0 02:21 ?        00:00:00 [bdi-default]
root        14     2  0 02:21 ?        00:00:00 [kintegrityd/0]
root        15     2  0 02:21 ?        00:00:00 [kblockd/0]
root        16     2  0 02:21 ?        00:00:00 [kacpid]
root        17     2  0 02:21 ?        00:00:00 [kacpi_notify]
root        18     2  0 02:21 ?        00:00:00 [kacpi_hotplug]
root        19     2  0 02:21 ?        00:00:00 [kseriod]
root        21     2  0 02:21 ?        00:00:00 [kondemand/0]
root        22     2  0 02:21 ?        00:00:00 [khungtaskd]
root        23     2  0 02:21 ?        00:00:00 [kswapd0]
root        24     2  0 02:21 ?        00:00:00 [ksmd]
root        25     2  0 02:21 ?        00:00:00 [aio/0]
root        26     2  0 02:21 ?        00:00:00 [crypto/0]
root       219     2  0 02:21 ?        00:00:00 [ata/0]
root       220     2  0 02:21 ?        00:00:00 [ata_aux]
root       221     2  0 02:21 ?        00:00:00 [scsi_eh_0]
root       222     2  0 02:21 ?        00:00:00 [scsi_eh_1]
root       294     2  0 02:21 ?        00:00:00 [aufsd/0]
root       295     2  0 02:21 ?        00:00:00 [aufsd_pre/0]
root       323     2  0 02:21 ?        00:00:00 [loop0]
root       738     1  0 02:21 ?        00:00:00 udevd --daemon
root       957     2  0 02:21 ?        00:00:00 [kpsmoused]
root       991   738  0 02:21 ?        00:00:00 udevd --daemon
root       992   738  0 02:21 ?        00:00:00 udevd --daemon
root      1307     1  0 02:21 ?        00:00:00 /usr/sbin/rsyslogd -c4
root      1341     1  0 02:21 ?        00:00:00 /usr/sbin/apache2 -k start
www-data  1344  1341  0 02:21 ?        00:00:00 /usr/sbin/apache2 -k start
root      1395     1  0 02:21 ?        00:00:00 /usr/sbin/cron
ntp       1397     1  0 02:21 ?        00:00:00 /usr/sbin/ntpd -p /var/run/nt
www-data  1423  1341  0 02:21 ?        00:00:00 /usr/sbin/apache2 -k start
www-data  1424  1341  0 02:21 ?        00:00:00 /usr/sbin/apache2 -k start
www-data  1425  1341  0 02:21 ?        00:00:00 /usr/sbin/apache2 -k start
www-data  1426  1341  0 02:21 ?        00:00:00 /usr/sbin/apache2 -k start
www-data  1427  1341  0 02:21 ?        00:00:00 /usr/sbin/apache2 -k start
root      1447     1  0 02:21 ?        00:00:00 /bin/sh /usr/bin/mysqld_safe
mysql     1555  1447  0 02:21 ?        00:00:02 /usr/sbin/mysqld --basedir=/u
root      1556  1447  0 02:21 ?        00:00:00 logger -t mysqld -p daemon.er
root      1618     1  0 02:21 tty1     00:00:00 /bin/login -f
root      1619     1  0 02:21 tty2     00:00:00 /bin/login -f
root      1620     1  0 02:21 tty3     00:00:00 /bin/login -f
root      1621     1  0 02:21 tty4     00:00:00 /bin/login -f
root      1622     1  0 02:21 tty5     00:00:00 /bin/login -f
root      1623     1  0 02:21 tty6     00:00:00 /bin/login -f
user      1626  1623  0 02:21 tty6     00:00:00 -bash
user      1628  1619  0 02:21 tty2     00:00:00 -bash
user      1629  1621  0 02:21 tty4     00:00:00 -bash
user      1630  1620  0 02:21 tty3     00:00:00 -bash
user      1631  1622  0 02:21 tty5     00:00:00 -bash
user      1633  1618  0 02:21 tty1     00:00:00 -bash
root      1727     1  0 02:21 ?        00:00:00 dhclient -v -pf /var/run/dhcl
root      1758     1  0 02:21 ?        00:00:00 /usr/sbin/sshd
root      1760  1633  0 02:21 tty1     00:00:00 sudo -s
root      1761  1760  0 02:21 tty1     00:00:00 /bin/bash
root      1845  1758  0 03:46 ?        00:00:00 sshd: user [priv]
user      1848  1845  0 03:46 ?        00:00:00 sshd: user@pts/0
user      1849  1848  0 03:46 pts/0    00:00:00 -bash
root      1910  1849  0 06:01 pts/0    00:00:00 sudo -s
root      1911  1910  0 06:01 pts/0    00:00:00 /bin/bash
root      1912  1911  0 06:01 pts/0    00:00:00 ps -edfAnother good way to list potentially interesting services is to use lsof to get the list of network connections and especially services listening on the network.
We can first work on the services listening on UDP:
# lsof -i UDP -n -P
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
ntpd     1397  ntp   16u  IPv4   3861      0t0  UDP *:123
ntpd     1397  ntp   17u  IPv6   3869      0t0  UDP *:123
ntpd     1397  ntp   18u  IPv4   3875      0t0  UDP 127.0.0.1:123
ntpd     1397  ntp   19u  IPv6   3876      0t0  UDP [::1]:123
ntpd     1397  ntp   20u  IPv4   5418      0t0  UDP 10.0.2.15:123
ntpd     1397  ntp   21u  IPv6   5419      0t0  UDP [fe80::5054:ff:fe12:3456]:123
dhclient 1727 root    6u  IPv4   3937      0t0  UDP *:68Here we can see:
- ntpdis running on port- UDP/123on IPv6 and IPv4.
- dhclientfor dynamic network configuration is running on port- UDP/68.
Then get information about the TCP services:
# lsof -i TCP -n -P
COMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
mysqld  1555    mysql   10u  IPv4   4073      0t0  TCP *:3306 (LISTEN)
sshd    1758     root    3u  IPv4   4966      0t0  TCP *:22 (LISTEN)
sshd    1758     root    4u  IPv6   4968      0t0  TCP *:22 (LISTEN)
sshd    1845     root    3r  IPv4   5615      0t0  TCP 10.0.2.15:22->10.0.2.2:52439 (ESTABLISHED)
sshd    1848     user    3u  IPv4   5615      0t0  TCP 10.0.2.15:22->10.0.2.2:52439 (ESTABLISHED)
apache2 1966     root    3u  IPv4   6502      0t0  TCP *:80 (LISTEN)
apache2 1969 www-data    3u  IPv4   6502      0t0  TCP *:80 (LISTEN)
apache2 1970 www-data    3u  IPv4   6502      0t0  TCP *:80 (LISTEN)
apache2 1971 www-data    3u  IPv4   6502      0t0  TCP *:80 (LISTEN)
apache2 1972 www-data    3u  IPv4   6502      0t0  TCP *:80 (LISTEN)Here we can see:
- Apache2is running and listening on the port- TCP/80on IPv4.
- mysqldis listening on port- TCP/3306on IPv4.
- sshdis running on port- TCP/22on IPv6 and IPv4.
- Someone (user)is connected to the SSH server.
OpenSSH
OpenSSH is running on this system, its configuration file can be found in /etc/ssh/ and is named sshd_config. The main thing to check for is the PermitRootLogin option that will prevent root to connect directly to the system. The default value of this option is yes, so you should ensure that it's set to no and that the line is not commented in the configuration file.
Additionally, it's wise to check that SSH version 1 is disabled, if it's not required. This line can be disabled by adding a line specifying only version 2 to be used:
protocol 2Since the system is available to the Internet, it's a good idea to change the default port (don't forget to put this new value in the firewall configuration) to make it less likely that automated brute-force/SSH scanners will locate this service. This can be done by changing the Port value in the configuration file (/etc/ssh/sshd_config).
Since this server is not a bounce box, AllowTcpForwarding should be set to no to prevent users from using this system to access other systems or to bypass network-layer security restrictions. The default value is yes, so you need to ensure that no is used and that the line is not commented out.
To update its configuration, the SSH server will need to be restarted.
Mysql
Using lsof, we saw that MySQL was listening on all interfaces, given the current use of the system, this is unnecessary, and to limit the system's attack surface, it's better to bind the socket on localhost to prevent external access to this service.
This can be done by adding the following option to the [mysqld] section of the /etc/mysql/my.cnf file:
[mysqld]
bind-address = 127.0.0.1To update its configuration, MySQL will need to be restarted.
We can now review the configuration of MySQL. First, we need to connect to MySQL, here we are lucky and no password is set for the root user:
$ mysql -u rootIf you don't have the password to connect as root to the database but you are root on the server, you can use the command strings on the Mysql users table stored in /var/lib/mysql/mysql/user.MYD to get the hashes:
# strings /var/lib/mysql/mysql/user.MYD
localhost
ot
127.0.0.1
ot
localhost
bian-sys-maint*70B6D2E96920B5E2BEE4AE1C719752A021342EF9
localhost wordpress*C260A4F79FA905AF65142FFE0B9A14FE0E1519CCOn Debian, a maintenance user named debian-sys-maint has access to the database and can be used to dump information from the database. You can find the password for this account in /etc/mysql/debian.cnf.
Once connected, we should first retrieve the version of the database:
mysql> select @@version;
+-------------------+
| @@version         |
+-------------------+
| 5.1.63-0+squeeze1 |
+-------------------+
1 row in set (0.00 sec)Based on this version, you can then search for any vulnerabilities in this release.
Next, you should get the list of users and their passwords:
mysql> SELECT Host, User, Password FROM mysql.user;
+-----------+------------------+-------------------------------------------+
| Host      | User             | Password                                  |
+-----------+------------------+-------------------------------------------+
| localhost | root             |                                           |
| 127.0.0.1 | root             |                                           |
| localhost | debian-sys-maint | *70B6D2E96920B5E2BEE4AE1C719752A021342EF9 |
| localhost | wordpress        | *C260A4F79FA905AF65142FFE0B9A14FE0E1519CC |
+-----------+------------------+-------------------------------------------+
4 rows in set (0.00 sec)Here we can see the following users have access to the database:
- rootfrom- localhost.
- debian-sys-maint.
- wordpress.
On MySQL, two algorithms are used to encrypt passwords:
- The old one, with this algorithm, encrypted passwords look like 43e9a4ab75570f5b.
- The new one, with this algorithm passwords will look like *4ACFE3202A5FF5CF467898FC58AAB1D61502944.
You can easily test this once you are connected to the database:
mysql> select password('admin') ;
+-------------------------------------------+
| password('admin')                         |
+-------------------------------------------+
| *4ACFE3202A5FF5CF467898FC58AAB1D615029441 |
+-------------------------------------------+
mysql> select old_password('admin') ;
+-----------------------+
| old_password('admin') |
+-----------------------+
| 43e9a4ab75570f5b      |
+-----------------------+The new version of the algorithm is obviously stronger than the old old and should be used.
Using John-The-Ripper, you will be able to test the strength of the MySQL passwords, you just need to copy them in a file with the correct format, as shown below:
$ cat mysql-password
wordpress:*C260A4F79FA905AF65142FFE0B9A14FE0E1519CCAnd crack them using the command mysql-sha1 (available in the community version).
Another important check is to review what users have the FILE privilege, as this privilege can be used by a user to access or create files on the system using MySQL. The list of users having the FILE privilege can be reviewed using the following SQL query:
mysql> SELECT user,file_priv FROM mysql.user WHERE FILE_PRIV='Y';
+------------------+-----------+
| user             | file_priv |
+------------------+-----------+
| root             | Y         |
| root             | Y         |
| root             | Y         |
| debian-sys-maint | Y         |
| wordpress        | Y         |
+------------------+-----------+
5 rows in set (0.00 sec)Here we can see that the user wordpress has the FILE privilege, this user is just used by the blog to store information, the FILE privilege is not needed here and should be removed.
Apache configuration
Since the server is used as a web server, it's important to review the configuration of the web server: Apache.
First, it's always good to check which user the web server is running as, you can find this out by running ps or by reviewing Apache's configuration and search for the values User and Group. Here they are available in /etc/apache2/apache2.conf:
# These need to be set in /etc/apache2/envvars
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}We can see that the values used are extracted from /etc/apache2/envvars:
export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-dataAll good, we have here a traditional Debian setup and the service is not running as root. Running a server as root will increase the consequences of a bug's exploitation in the service itself or in any web application hosted.
To ensure that no local users can modify the hosted website and to harden exploitation of possible vulnerabilities, it's always a good idea to limit access to the webroot. Here, we can see that the DocumentRoot is /var/www/wordpress, using ls, we can check that the permissions (which seems are not correctly set):
# ls -lR /var/www/wordpress
/var/www/wordpress/:
total 125
-rwxrwxrwx 1 www-data www-data   395 Jul 11 07:02 index.php
-rwxrwxrwx 1 www-data www-data 19929 Jul 11 07:02 license.txt
-rwxrwxrwx 1 www-data www-data  9177 Jul 11 07:02 readme.html
-rwxrwxrwx 1 www-data www-data  4264 Jul 11 07:02 wp-activate.php
drwxrwxrwx 9 www-data www-data  1913 Aug  6 03:26 wp-admin
-rwxrwxrwx 1 www-data www-data  1354 Jul 11 07:02 wp-app.php
-rwxrwxrwx 1 www-data www-data   271 Jul 11 07:02 wp-blog-header.php
-rwxrwxrwx 1 www-data www-data  3522 Jul 11 07:02 wp-comments-post.php
-rwxrwxrwx 1 www-data www-data  3456 Jul 11 21:51 wp-config.php
[ ... ]These files should not be available to any user. In order to restrict the risk of the overwrite by the web server, the complete application ownership can be given to a third user. This way, the web server will not be able to modify the web application. This is however likely to break some applications.
As a best practice, it's always good to avoid extra-information in the server headers. To prevent Apache from leaking to much information, the following parameters should be reviewed:
- ServerTokens: the recommended value is- Prod.
- ServerSignature: should be turned- Off.
On Debian, these two parameters are configured in the /etc/apache2/conf.d/security.
To limit the server exposure and information leak, it's always good to disable directory listing. Directory listing is configured by the parameter Indexes. By reviewing the configuration of the website available in /etc/apache2/sites-enable/000-default, we can see that directory listing is enabled:
<Directory /var/www/wordpress/>
  Options Indexes FollowSymLinks MultiViews
  AllowOverride None
  Order allow,deny
  allow from all
</Directory>You can access the following page to confirm this setting: http://vulnerable/wp-admin/images/.
The configuration should be reviewed to replace Indexes by -Indexes, to prevent potential information leak.
We can see that PHP is enabled on this server:
# ls /etc/apache2/mods-enabled/php*
/etc/apache2/mods-enabled/php5.conf
/etc/apache2/mods-enabled/php5.loadWe can also see that PHP is disabled for user public directories:
# cat /etc/apache2/mods-enabled/php5.conf
<IfModule mod_php5.c>
   <FilesMatch "\.ph(p3?|tml)$">
     SetHandler application/x-httpd-php
   </FilesMatch>
   <FilesMatch "\.phps$">
     SetHandler application/x-httpd-php-source
   </FilesMatch>
   # To re-enable php in user directories comment the following lines
   # (from <IfModule ...> to </IfModule>.) Do NOT set it to On as it
   # prevents .htaccess files from disabling it.
   <IfModule mod_userdir.c>
       <Directory /home/*/public_html>
           php_admin_value engine Off
       </Directory>
   </IfModule>
</IfModule>This is a good practice since it will prevent users from running PHP code in their own public_directory (/~username/), which allows them to jump from their user ID to that of the web server.
PHP configuration
Here, we are going to focus on the PHP configuration used by Apache2. We can see in the Apache configuration that the following file is loaded:
# cat /etc/apache2/mods-enabled/php5.load
LoadModule php5_module /usr/lib/apache2/modules/libphp5.soIf we run strings on that binary and search for apache2, we can see some interesting information:
# strings /usr/lib/apache2/modules/libphp5.so  | grep apache2
/etc/php5/apache2
/etc/php5/apache2/conf.d
/tmp/buildd/php5-5.3.3/sapi/apache2handler/mod_php5.c
apache2hook_post_config
apache2handler
/tmp/buildd/php5-5.3.3/sapi/apache2handler/sapi_apache2.cWe can see two paths that are actually where PHP will look for its configuration. By reading the Debian documentation, we can confirm that the PHP configuration used by Apache2 is stored in the /etc/php5/apache2 directory.
First, as we saw for Apache, it's always a good idea to hide the value of PHP in use. The parameter expose_php tells PHP to add an extra HTTP header X-Powered-By in all responses. It's better to hide this information by turning expose_php off.
Furthermore, the following options need to be reviewed:
- display_errors: should be turned- Offin production.
- error_reporting: should be set to- E_ALL.
- log_errors: should be turned- On.
- safe_mode: can be bypassed but it will definitely slow down an attacker; it should be turned- On.
- disable_functions: can be used to block access to sensitive functions like:- eval,- exec,- passthru,- shell_exec,- system,- proc_open,- popen...
- allow_url_include: should be turned- Off.
As often on recent version of Debian, Suhosin is installed on this server. Suhosin adds an extra-layer of protection for the PHP engine and all hosted PHP applications.
We will first remove all commented and empty lines, to get a simple export of the Suhosin configuration:
# egrep -v -e '^;|^$' /etc/php5/apache2/conf.d/suhosin.ini
extension=suhosin.so
[suhosin]Here, we can see that no options have been set, Suhosin is in its default configuration.
The following options provided by Suhosin can be enabled to increase the security of the PHP application:
- suhosin.log.syslog: to log to- syslogusing the value- S_ALL.
- suhosin.executor.include.max_traversal: to prevent directory traversal, a value of- 3should prevent and limit an attack's impact while keeping a working environment.
- suhosin.executor.disable_evaland- suhosin.executor.disable_emodifier: to disable the function- evaland possible code execution using- preg_replace- /emodifier.
More options can be found in Suhosin documentation.
Crontab
Crontab is used on Linux/UNIX to run tasks at a given time.  You can find the list of crontabs in /var/spool/cron/crontabs or using the command crontab -u <user> -l to list the tasks for a given user.
One of the important thing to look for is the permissions on scripts called by a task. For example, here the script /root/backup.sh is automatically ran in root's task (/var/spool/cron/crontabs/root):
0 2 * * * /root/backup.shWe can check the permissions on this file and see that anyone can modify it:
# ls -l /root/backup.sh
-rwxrwxrwx 1 root root 84 Aug  6 03:38 /root/backup.shThe permissions on this script should be modified to prevent any user from modifying the script to gain root access the next time the task is ran.
It's also important to avoid sensitive tasks between 2am and 3am or they will be ran 0 or 2 times during daylight savings (expect if the system timezone does not have daylight savings).
Conclusion
This course showed you how to review a Linux system to find potential security issues and how to correct them. I hope the course provides you with more details on how a Linux system can be insecurely configured and how you can quickly improve its security. This example is quiet simple but is representative of most web servers you will find on the Internet. Now it's time to apply what you've just learned on your own systems!
I hope you enjoyed learning with PentesterLab.